package com.punch.order.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.punch.common.dto.order.OrderDTO;
import com.punch.common.dto.order.OrderDetailDTO;
import com.punch.common.enums.order.*;
import com.punch.common.exception.BusinessException;
import com.punch.common.exception.CommonExceptionEnum;
import com.punch.common.feign.FeignClassService;
import com.punch.common.page.PageInfo;
import com.punch.common.query.order.OrderAPPQueryInfo;
import com.punch.common.redis.utils.UserUtil;
import com.punch.common.tools.BasePage;
import com.punch.common.vo.order.CourseClassVO;
import com.punch.common.vo.order.CourseGoodsVO;
import com.punch.common.vo.order.OrderAppDetailVO;
import com.punch.common.vo.order.OrderAppPageVO;
import com.punch.order.constants.CurrencyEnum;
import com.punch.order.constants.OrderTypeEnum;
import com.punch.order.constants.PayChannelEnum;
import com.punch.order.design.OrderContextFactory;
import com.punch.order.design.PayContext;
import com.punch.order.exception.OrderExceptionEnum;
import com.punch.order.mapper.CommonOrderMapper;
import com.punch.order.model.CommonOrder;
import com.punch.order.model.OrderCourseSnapshot;
import com.punch.order.model.OrderGoodsSnapshot;
import com.punch.order.service.CommonOrderDetailService;
import com.punch.order.service.CommonOrderService;
import com.punch.order.service.OrderCourseSnapshotService;
import com.punch.order.service.OrderGoodsSnapshotService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * <p>
 * 通用订单 服务实现类
 * </p>
 *
 * @author wjj
 * @since 2020-12-09
 */
@Service
@Slf4j
public class CommonOrderServiceImpl extends ServiceImpl<CommonOrderMapper, CommonOrder> implements CommonOrderService {

    @Resource
    private OrderCourseSnapshotService orderCourseSnapshotService;

    @Resource
    private OrderGoodsSnapshotService orderGoodsSnapshotService;

    @Resource
    private OrderContextFactory orderContextFactory;

    @Resource
    private CommonOrderDetailService commonOrderDetailService;

    @Resource
    private FeignClassService feignClassService;

    /**
     * 创建订单
     *
     * @param orderDTO 创建订单
     * @return json
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject createOrder(OrderDTO orderDTO) {
        long startTime = System.currentTimeMillis();
        //订单类型是否支持
        Integer orderType = orderDTO.getOrderType();
        if (StrUtil.isBlank(OrderTypeEnum.getName(orderType))) {
            throw new BusinessException(OrderExceptionEnum.PAY_ORDER_TYPE_NOT_SUPPORT);
        }
        // 检查渠道是否支持
        boolean isChannelSupport = false;
        String payChannel = orderDTO.getPayChannel().toUpperCase();
        //微信app
        if (PayChannelEnum.WXPAY_APP.name().equals(payChannel)) {
            isChannelSupport = true;
        }
        //支付宝app
        if (PayChannelEnum.ALIPAY_APP.name().equals(payChannel)) {
            isChannelSupport = true;
        }
        //0元订单
        if (PayChannelEnum.ZERO.name().equals(payChannel)) {
            isChannelSupport = true;
        }
        if (!isChannelSupport) {
            throw new BusinessException(OrderExceptionEnum.PAY_CHANNEL_NOT_SUPPORT);
        }
        //订单信息预处理
        payPretreatment(orderDTO, orderType);
        //支付策略模式调用
        PayContext orderSignContext = orderContextFactory.getContext(payChannel);
        JSONObject jsonObject = orderSignContext.payOrder(orderDTO);
        log.info("创建订单花费时间[{}]", System.currentTimeMillis() - startTime);
        return jsonObject;
    }

    /**
     * 修改订单状态
     *
     * @param orderSN 变化
     * @param status  状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderStatus(String orderSN, int status) {
        CommonOrder commonOrder = new CommonOrder();
        commonOrder.setOrderStatus(status);
        UpdateWrapper<CommonOrder> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(CommonOrder::getOrderSN, orderSN);
        update(commonOrder, updateWrapper);
        OrderCourseSnapshot orderCourseSnapshot = new OrderCourseSnapshot();
        orderCourseSnapshot.setStatus(status);
        UpdateWrapper<OrderCourseSnapshot> snapshotUpdateWrapper = new UpdateWrapper<>();
        snapshotUpdateWrapper.lambda().eq(OrderCourseSnapshot::getOrderSN, orderSN);
        orderCourseSnapshotService.update(orderCourseSnapshot, snapshotUpdateWrapper);
    }

    /**
     * 修改订单状态
     *
     * @param orderId 订单id
     * @param status  状态
     */
    @Override
    public void updateOrderStatus(Long orderId, int status) {
        CommonOrder commonOrder = new CommonOrder();
        commonOrder.setOrderStatus(status);
        commonOrder.setId(orderId);
        updateById(commonOrder);
        OrderCourseSnapshot orderCourseSnapshot = new OrderCourseSnapshot();
        orderCourseSnapshot.setStatus(status);
        UpdateWrapper<OrderCourseSnapshot> snapshotUpdateWrapper = new UpdateWrapper<>();
        snapshotUpdateWrapper.lambda().eq(OrderCourseSnapshot::getOrderId, orderId);
        orderCourseSnapshotService.update(orderCourseSnapshot, snapshotUpdateWrapper);
    }

    /**
     * APP端查询订单
     *
     * @param queryInfo 查询条件
     * @return PageInfo<OrderAppPageVO>
     */
    @Override
    public PageInfo<OrderAppPageVO> listOrder(OrderAPPQueryInfo queryInfo) {
        queryInfo.setCreateId(UserUtil.getLoginUserId());
        return BasePage.queryBasePage(queryInfo, params -> this.getBaseMapper().queryCount(params),
                params -> {
                    List<OrderAppPageVO> retList = this.getBaseMapper().queryList(params);
                    retList.forEach(o -> {
                        o.setAddress(o.getSchoolName() + o.getClassRoomName());
                        //只有待付款的需要传创建时间字段
                        if (o.getOrderStatus() != OrderStatusEnum.TO_PAY.getCode()) {
                            o.setCreateTime(null);
                        }
                    });
                    return retList;
                });
    }

    /**
     * 获取订单先去
     *
     * @param orderId 订单id
     * @return OrderAppDetailVO
     */
    @Override
    public OrderAppDetailVO getOrder(Long orderId) {
        long startTime = System.currentTimeMillis();
        OrderAppDetailVO orderAppDetailVO = new OrderAppDetailVO();
        CommonOrder commonOrderEX = getById(orderId);
        Optional.ofNullable(commonOrderEX).orElseThrow(() -> new BusinessException(OrderExceptionEnum.ORDER_NOT_EXIST));
        orderAppDetailVO.setOrderId(orderId);
        orderAppDetailVO.setOrderSN(commonOrderEX.getOrderSN());
        orderAppDetailVO.setCreateTime(commonOrderEX.getCreateTime());
        orderAppDetailVO.setTotalAmount(commonOrderEX.getTotalAmount());
        orderAppDetailVO.setPayType(PayChannelEnum.getName(commonOrderEX.getPayChannel()));//特殊处理下
        //获取课程信息
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(OrderCourseSnapshot::getOrderId, orderId);
        OrderCourseSnapshot orderCourseSnapshotEX = orderCourseSnapshotService.getOne(queryWrapper);
        Optional.ofNullable(orderCourseSnapshotEX).orElseThrow(() -> new BusinessException(OrderExceptionEnum.ORDER_NOT_EXIST));
        CourseClassVO courseClassVO = new CourseClassVO();
        courseClassVO.setCourseImg(orderCourseSnapshotEX.getCourseImg());
        courseClassVO.setCourseClassName(orderCourseSnapshotEX.getCourseClassName());
        courseClassVO.setCourseTotalAmount(orderCourseSnapshotEX.getCourseTotalAmount());
        courseClassVO.setStudentName(orderCourseSnapshotEX.getStudentName());
        courseClassVO.setAddress(orderCourseSnapshotEX.getSchoolName() + orderCourseSnapshotEX.getClassRoomName());
        courseClassVO.setCourseTime(orderCourseSnapshotEX.getCourseTime());
        orderAppDetailVO.setCourseClassVO(courseClassVO);
        queryWrapper = null;
        //获取教材教具信息
        if (orderCourseSnapshotEX.getGoodsFlag() == 1) {
            QueryWrapper<OrderGoodsSnapshot> goodsSnapshotQueryWrapper = new QueryWrapper<>();
            goodsSnapshotQueryWrapper.lambda().eq(OrderGoodsSnapshot::getOrderId, orderId);
            OrderGoodsSnapshot orderGoodsEX = orderGoodsSnapshotService.getOne(goodsSnapshotQueryWrapper);
            Optional.ofNullable(orderGoodsEX).orElseThrow(() -> new BusinessException(OrderExceptionEnum.ORDER_NOT_EXIST));
            CourseGoodsVO courseGoodsVO = new CourseGoodsVO();
            courseGoodsVO.setGoodsCover(orderGoodsEX.getGoodsCover());
            courseGoodsVO.setGoodsName(orderGoodsEX.getGoodsName());
            courseGoodsVO.setGoodsTitle(orderGoodsEX.getGoodsTitle());
            courseGoodsVO.setGoodsAmount(orderGoodsEX.getGoodsAmount());
            orderAppDetailVO.setCourseGoodsVO(courseGoodsVO);
            goodsSnapshotQueryWrapper = null;
            orderGoodsEX = null;//help gc
        }
        orderCourseSnapshotEX = null;
        log.info("获取订单详情[{}]", System.currentTimeMillis() - startTime);
        return orderAppDetailVO;
    }

    /**
     * 支付完成
     *
     * @param orderId 订单id
     */
    @Override
    public void finishPayOrder(Long orderId) {
        CommonOrder commonOrder = getById(orderId);
        Optional.of(commonOrder).orElseThrow(() -> new BusinessException(OrderExceptionEnum.ORDER_NOT_EXIST));
        if (commonOrder.getOrderStatus() != OrderStatusEnum.TO_PAY.getCode()) {
            throw new BusinessException(OrderExceptionEnum.ORDER_STATUS_ERROR);
        }
        updateOrderStatus(orderId, OrderStatusEnum.HAVE_PAY.getCode());
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(OrderCourseSnapshot::getOrderId, orderId);
        OrderCourseSnapshot orderCourseSnapshot = orderCourseSnapshotService.getOne(queryWrapper);
        Boolean flag = feignClassService.updateSignUpNumber(orderCourseSnapshot.getCourseClassId());
        if (!flag) {
            throw new BusinessException(OrderExceptionEnum.UPDATE_SIGN_UP_NUMBER_ERROR);
        }
    }

    /**
     * 开班修改订单状态
     *
     * @param classId 课程班级id
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean startClass(Long classId) {
        QueryWrapper<OrderCourseSnapshot> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(OrderCourseSnapshot::getCourseClassId, classId)
                .eq(OrderCourseSnapshot::getStatus, OrderStatusEnum.HAVE_PAY.getCode());
        List<OrderCourseSnapshot> list = orderCourseSnapshotService.list(queryWrapper);
        int status = OrderStatusEnum.OPEN_CLASS.getCode();
        list.forEach(courseSnapshot -> {
            OrderCourseSnapshot order = new OrderCourseSnapshot();
            order.setId(courseSnapshot.getId());
            order.setStatus(status);
            updateOrderStatus(courseSnapshot.getOrderId(), status);
        });
        return true;
    }

    /**
     * 生成流水号
     *
     * @return 流水号
     */
    private static String generateOrderNo() {
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        return "SO" + snowflake.nextIdStr();
    }

    /**
     * 处理订单信息,需要插入common_order,common_order_detail
     *
     * @param orderDTO 订单表单
     */
    private void payPretreatment(OrderDTO orderDTO, Integer orderType) {
        //插入common_order,common_order_detail表
        String orderSN = generateOrderNo();
        Long orderId;
        Long userId = UserUtil.getLoginUserId();
        //主订单
        orderId = saveMainOrder(orderDTO, orderSN, userId);
        //订单详情
        List<OrderDetailDTO> productList = orderDTO.getProductList();
        commonOrderDetailService.saveDetail(productList, userId, orderSN, orderId);
        //三点半课程订单,后期有可能有不同的订单
        if (orderType == 1) {
            orderCourseSnapshotService.createOrderCourse(orderDTO, userId, orderId, orderSN);
        }
        orderDTO.setOrderSN(orderSN);
    }

    /**
     * 保存主订单
     *
     * @param orderDTO 订单信息
     * @param orderSN  订单序列号
     * @param userId   用户uid
     * @return long
     */
    private Long saveMainOrder(OrderDTO orderDTO, String orderSN, Long userId) {
        CommonOrder commonOrder = new CommonOrder();
        commonOrder.setOrderSN(orderSN);
        commonOrder.setOrderTime(LocalDateTime.now());//下单时间
        commonOrder.setOrderType(orderDTO.getOrderType());//订单类型
        commonOrder.setTotalAmount(orderDTO.getTotalAmount());//总金额
        commonOrder.setOrderAmount(orderDTO.getOrderAmount());//需要支付金额
        commonOrder.setOriginalAmount(orderDTO.getOriginalAmount());//所有产品相加金额
        commonOrder.setCurrency(CurrencyEnum.CNY.name());
        commonOrder.setOrderStatus(OrderStatusEnum.TO_PAY.getCode());//订单状态待付款
        commonOrder.setCancelStatus(CancelStatusEnum.NONE.getCode());//取消状态
        commonOrder.setPartRefundFlag(PartRefundFlagEnum.NONE.getCode());
        commonOrder.setRefundStatus(RefundStatusEnum.NONE.getCode());
        commonOrder.setPayStatus(PayStatusEnum.TO_PAY.getCode());//支付状态
        commonOrder.setPayType(orderDTO.getPayType());//支付类型 0-(0元订单)无支付 1-在线支付
        commonOrder.setPayChannel(orderDTO.getPayChannel());//支付渠道 ZERO-无支付 ALIPAY_APP-支付宝APP,WXPAY_APP-微信APP
        commonOrder.setCreateId(userId);
        LocalDateTime createTime = LocalDateTime.now();
        commonOrder.setCreateTime(createTime);
        if (!save(commonOrder)) {
            throw new BusinessException(CommonExceptionEnum.SAVE_ERROR);
        }
        orderDTO.setOrderId(commonOrder.getCreateId());
        orderDTO.setCreateTime(createTime);
        return commonOrder.getId();
    }
}
